#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <oci.h>


typedef struct {
	OCIEnv		*envhp;			/* Environment handle 	*/
	OCIError	*errhp;			/* Error handle		*/
	OCIServer	*srvhp;			/* Server handle	*/
	OCISession	*usrhp;			/* User handle		*/
	OCISvcCtx	*svchp;			/* Service Context	*/
} ServerContext;


ServerContext *Connect(char *user, char *password, char *database) {
	ServerContext *sc;
	int	userlen;
	int	passwordlen;
	int	databaselen;
	sword	status;
	sword	credentials = OCI_CRED_RDBMS;

        userlen = strlen(user);
        passwordlen = strlen(password);
        databaselen = strlen(database);

        sc = (ServerContext *)malloc(sizeof(ServerContext));

	sc->envhp = NULL;
	sc->errhp = NULL;
	sc->svchp = NULL;
	sc->srvhp = NULL;
	sc->usrhp = NULL;

	status = OCIEnvCreate(&sc->envhp, OCI_THREADED, NULL, NULL, NULL, NULL,
                              0, NULL);
	assert(status == OCI_SUCCESS);

	/* Allocate the error handle */
	status = OCIHandleAlloc(sc->envhp, (dvoid **)&sc->errhp,
                                OCI_HTYPE_ERROR, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Allocate the server handle */
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->srvhp,
                                OCI_HTYPE_SERVER, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Connect to database */
	status = OCIServerAttach(sc->srvhp, sc->errhp, database, databaselen,
                                 OCI_DEFAULT);
	assert(status == OCI_SUCCESS);

	/* Allocate a service context */
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->svchp,
                                OCI_HTYPE_SVCCTX, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Attach the server to the service context */
	status = OCIAttrSet(sc->svchp, OCI_HTYPE_SVCCTX, (dvoid *) sc->srvhp,
                            (ub4) 0, OCI_ATTR_SERVER, sc->errhp);
	assert(status == OCI_SUCCESS);

	/* Allocate a session */
	status = OCIHandleAlloc(sc->envhp, (dvoid **) &sc->usrhp,
                                OCI_HTYPE_SESSION, 0, NULL);
	assert(status == OCI_SUCCESS);

	/* Were credentials provided? if so, set them in the session */
        status = OCIAttrSet(sc->usrhp, OCI_HTYPE_SESSION,
                            (dvoid *) user, (ub4) userlen,
                            OCI_ATTR_USERNAME, sc->errhp);
        assert(status == OCI_SUCCESS);

        status = OCIAttrSet(sc->usrhp, OCI_HTYPE_SESSION,
                            (dvoid *) password, (ub4) passwordlen,
                            OCI_ATTR_PASSWORD, sc->errhp);
        assert(status == OCI_SUCCESS);
	
	/* Now begin the session */
	status = OCISessionBegin(sc->svchp, sc->errhp, sc->usrhp,
                                 credentials, OCI_DEFAULT);
	assert(status == OCI_SUCCESS);

	/* Now attach the session to the service context */
	status = OCIAttrSet(sc->svchp, OCI_HTYPE_SVCCTX,
                            sc->usrhp, 0, OCI_ATTR_SESSION, sc->errhp);
	assert(status == OCI_SUCCESS);

	return sc;
}

/*
** ServerContext_dealloc
**
** Called when a server context goes out of scope; hang up the connection
** to the database!
*/

void ServerContext_dealloc(ServerContext *self) {
	sword status;

	/* End the session */
	assert(self->svchp);
        status = OCISessionEnd(self->svchp, self->errhp, self->usrhp,
                               OCI_DEFAULT);
        assert(status == OCI_SUCCESS);

	/* Disconnect from the server */
	assert(self->srvhp);
        status = OCIServerDetach(self->srvhp, self->errhp, OCI_DEFAULT);
        assert(status == OCI_SUCCESS);

	/* Deallocate the session handle */
        assert(self->usrhp);
        status = OCIHandleFree(self->usrhp, OCI_HTYPE_SESSION);
        assert(status == OCI_SUCCESS);

	/* Deallocate the server handle */
        assert(self->srvhp);
        status = OCIHandleFree(self->srvhp, OCI_HTYPE_SERVER);
        assert(status == OCI_SUCCESS);

	/* Deallocate the service context handle */
	assert(self->svchp);
        status = OCIHandleFree(self->svchp, OCI_HTYPE_SVCCTX);
        assert(status == OCI_SUCCESS);

	/* Deallocate the error handle */
        assert(self->errhp);
        status = OCIHandleFree(self->errhp, OCI_HTYPE_ERROR);
        assert(status == OCI_SUCCESS);

	/* Deallocate the environment handle */
        assert(self->envhp);
        status = OCIHandleFree(self->envhp, OCI_HTYPE_ENV);
        assert(status == OCI_SUCCESS);

        free(self);
}

int main(int argc, char *argv[]) {
    char *user;
    char *password;
    char *database;
    ServerContext *db1;
    ServerContext *db2;
    assert(argc == 4);
    user = argv[1];
    password = argv[2];
    database = argv[3];
    db1 = Connect(user, password, database);
    db2 = Connect(user, password, database);
    fprintf(stderr, "Deleting db2.\n");
    ServerContext_dealloc(db2);
    fprintf(stderr, "Deleting db1.\n");
    ServerContext_dealloc(db1);
    fprintf(stderr, "This line often not reached.\n");
    return 0;
}

